﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using RaytheonTimeDataModel;
using System.Data.Objects;

namespace RaytheonTimeWindows
{
    public partial class frmEditWorktime : Form
    {
        #region private data members

        private Worktime worktime_;
        private int worktimeId_;
        private bool isNew_ = true;
        private RaytheonTimeDataEntities db_;
        private RaytheonTimeDataEntities workdayDb_;
        private BindingList<ChargeNumber> chargeNumberBindingList_;
        private BindingList<ChargeNumberAttribute> chargeNumberAttributeBindingList_;

        public Common.DataChangedDelegate DataChangedCallback;

        #endregion

        #region Constructors

        private frmEditWorktime()
        {
            db_ = new RaytheonTimeDataEntities();
            workdayDb_ = new RaytheonTimeDataEntities();
            worktimeId_ = 0;
        }

        public frmEditWorktime(Int32 workdayId, DateTime date, Int32 employeeId)
            : this()
        {
            InitializeComponent();
            worktime_ = new Worktime();

            var workday = (from w in db_.Workdays
                           where w.workdayId == workdayId
                           select w).FirstOrDefault();
            worktime_.Workday = workday;
            worktime_.workdayId = workday.workdayId;
            worktimeId_ = 0;
        }

        public frmEditWorktime(Int32 worktimeId)
            : this()
        {
            InitializeComponent();

            isNew_ = false;
            worktime_ = (from w in db_.Worktimes
                         where w.worktimeId == worktimeId
                         select w).FirstOrDefault();
            worktimeId_ = worktimeId;
        }

        #endregion

        #region Overriden Event Handlers

        protected override void OnLoad(EventArgs e)
        {
            try
            {
                base.OnLoad(e);

                LoadForm();
            }
            catch (Exception ex)
            {
                String message = "Error occurred Loading frmEditWorktime\n";
                message += ex.Message;
                MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);

            try
            {
                if (CheckForChanges())
                {
                    DialogResult dgResult;
                    dgResult = MessageBox.Show("Record not saved; are you sure you want to exit?",
                                                "Unsaved Record Warning", MessageBoxButtons.YesNo,
                                                MessageBoxIcon.Warning);
                    if (dgResult == DialogResult.No)
                    {
                        e.Cancel = true;
                    }
                }
            }
            catch (Exception ex)
            {
                String message = "Error occurred Closing\n";
                message += ex.Message;
                MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #endregion

        #region Private Event Handlers

        private void frmEditWorktime_KeyDown(object sender, KeyEventArgs e)
        {
            try
            {
                if (e.KeyCode == Keys.Escape)
                {
                    this.Close();
                }
            }
            catch (Exception ex)
            {
                String message = "Error occurred Pressing the Escape Key\n";
                message += ex.Message;
                MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            try
            {
                this.Close();
            }
            catch (Exception ex)
            {
                String message = "Error occurred clicking on Cancel\n";
                message += ex.Message;
                MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void btnOk_Click(object sender, EventArgs e)
        {
            try
            {
                SaveData();
            }
            catch (Exception ex)
            {
                String message = "Error occurred clicking on OK\n";
                message += ex.Message;
                MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void cmbChargeNumber_SelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                if (cmbChargeNumber.SelectedItem != null)
                {
                    ChargeNumber chargeNumber = (ChargeNumber)cmbChargeNumber.SelectedValue;
                    txtNetworkId.Text = chargeNumber.network.Trim();
                    txtActivityId.Text = chargeNumber.activity.Trim();
                }
            }
            catch (Exception ex)
            {
                String message = "Error occurred selecting a charge number\n";
                message += ex.Message;
                MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #endregion

        #region Private Methods

        private void LoadForm()
        {
            try
            {
                txtWorktimeId.Text = worktime_.worktimeId.ToString();
                txtWorkdayId.Text = worktime_.workdayId.ToString();
                if (worktime_.chargeNumberId != null && worktime_.chargeNumberId > 0)
                {
                    txtNetworkId.Text = worktime_.ChargeNumber.network.Trim();
                    txtActivityId.Text = worktime_.ChargeNumber.activity.Trim();
                }
                maskedTxtTimeStart.Text = worktime_.timeStart.ToString("HH:mm");
                maskedTxtTimeEnd.Text = (worktime_.timeStart.AddHours(worktime_.duration)).ToString("HH:mm");
                if (worktime_.notes != null)
                {
                    txtNotes.Text = worktime_.notes.Trim();
                }
                chkFridayForwarded.Checked = worktime_.isFridayForwarded;

                txtNetworkId.AutoCompleteCustomSource.Clear();
                txtActivityId.AutoCompleteCustomSource.Clear();

                var networks = (from c in db_.ChargeNumbers
                                where c.isDeleted == false
                                select c.network).ToArray();

                var activities = (from c in db_.ChargeNumbers
                                  where c.isDeleted == false
                                  select c.activity).ToArray();

                // TODO: Should I union this with the entire list in ChargeNumbers (which may be a superset)?
                var chargeNumbers = (from c in db_.ChargeNumbers
                                     join wt in db_.Worktimes on c.chargeNumberId equals wt.chargeNumberId
                                     join wd in db_.Workdays on wt.workdayId equals wd.workdayId
                                     join e in db_.Employees on wd.employeeId equals e.employeeId
                                     where c.isDeleted == false &&
                                           e.employeeNumber.Trim() == worktime_.Workday.Employee.employeeNumber.Trim()
                                     orderby wt.timeStart descending
                                     select c).Distinct();

                var chargeNumberAttributes = (from c in db_.ChargeNumberAttributes
                                              where c.isDeleted == false
                                              orderby c.attributeOrder
                                              select c);

                txtNetworkId.AutoCompleteCustomSource.AddRange(networks);
                txtActivityId.AutoCompleteCustomSource.AddRange(activities);

                chargeNumberBindingList_ = new BindingList<ChargeNumber>(chargeNumbers.ToList());
                chargeNumberAttributeBindingList_ = new BindingList<ChargeNumberAttribute>(chargeNumberAttributes.ToList());

                cmbChargeNumber.DataSource = chargeNumberBindingList_;
                cmbChargeNumber.DisplayMember = "NetworkActivity";
                if (worktime_.chargeNumberId != null && worktime_.chargeNumberId > 0)
                {
                    cmbChargeNumber.SelectedItem = worktime_.ChargeNumber;
                }
                else
                {
                    cmbChargeNumber.SelectedIndex = -1;
                    cmbChargeNumber.Text = String.Empty;
                    txtNetworkId.Text = String.Empty;
                    txtActivityId.Text = String.Empty;
                }

                cmbAttAbsText.DataSource = chargeNumberAttributeBindingList_;
                cmbAttAbsText.DisplayMember = "attributeText";
                if (worktime_.attributeId > 0)
                {
                    cmbAttAbsText.SelectedItem = worktime_.ChargeNumberAttribute;
                }
                else
                {
                    cmbAttAbsText.SelectedIndex = -1;
                    cmbAttAbsText.Text = String.Empty;
                }

                if (worktime_.Workday.workdayDate.DayOfWeek == DayOfWeek.Friday)
                {
                    lblFridayForward.Visible = true;
                    chkFridayForward.Visible = true;

                    // do not allow the user to reforward.
                    if (worktime_.isFridayForwarded)
                    {
                        chkFridayForward.Enabled = false;
                    }
                }
                else
                {
                    lblFridayForward.Visible = false;
                    chkFridayForward.Visible = false;
                }
            }
            catch (Exception ex)
            {
                String message = "Error occurred converting calling Loading frmEditWorktime\n";
                message += ex.Message;
                MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private bool CheckForChanges()
        {
            using (var db = new RaytheonTimeDataEntities())
            {
                var worktime = (from w in db.Worktimes
                                where w.worktimeId == worktimeId_
                                select w).FirstOrDefault();

                if (worktime == null)
                {
                    return false;
                }

                if (cmbAttAbsText.SelectedItem != null && cmbAttAbsText.SelectedItem is ChargeNumberAttribute)
                {
                    if (worktime.ChargeNumberAttribute == null ||
                        ((ChargeNumberAttribute)cmbAttAbsText.SelectedItem).attributeId != worktime.ChargeNumberAttribute.attributeId)
                    {
                        return true;
                    }
                }

                if (((worktime.ChargeNumber == null && txtNetworkId.Text.Trim().Length > 0) ||
                     (worktime.ChargeNumber != null && txtNetworkId.Text.Trim() != worktime.ChargeNumber.network.Trim())))
                {
                    return true;
                }

                if (((worktime.ChargeNumber == null && txtActivityId.Text.Trim().Length > 0) ||
                     (worktime.ChargeNumber != null && txtActivityId.Text.Trim() != worktime.ChargeNumber.activity.Trim())))
                {
                    return true;
                }

                if ((maskedTxtTimeStart.Text.Trim() != worktime.timeStart.ToString("HH:mm")))
                {
                    return true;
                }

                if (maskedTxtTimeEnd.Text.Trim() != worktime.timeStart.AddHours(worktime.duration).ToString("HH:mm"))
                {
                    return true;
                }

                if (((worktime.notes == null && txtNotes.Text.Trim().Length > 0) ||
                    (txtNotes.Text.Trim() != worktime.notes.Trim())))
                {
                    return true;
                }
            }

            return false;
        }

        private void SaveData()
        {
            bool isValid = true;

            if (isNew_ || CheckForChanges())
            {

                if (isNew_)
                {
                    db_.Worktimes.Add(worktime_);
                }

                if (txtNetworkId.Text.Trim().Length == 0)
                {
                    // I am allowing no charge numbers
                    // TODO: add more intelligent checks for this
                    worktime_.chargeNumberId = null;
                }
                else
                {
                    String network = txtNetworkId.Text.Trim();
                    String activity = txtActivityId.Text.Trim();

                    var chargeNumber = (from c in db_.ChargeNumbers
                                        where c.network == network &&
                                              c.activity == activity
                                        select c).FirstOrDefault();
                    if (chargeNumber != null)
                    {
                        chargeNumber.isDeleted = false;
                        worktime_.chargeNumberId = chargeNumber.chargeNumberId;
                    }
                    else
                    {
                        chargeNumber = new ChargeNumber();
                        chargeNumber.network = network;
                        chargeNumber.activity = activity;
                        chargeNumber.isDeleted = false;
                        workdayDb_.ChargeNumbers.Add(chargeNumber);
                        workdayDb_.SaveChanges();
                        worktime_.chargeNumberId = chargeNumber.chargeNumberId;
                    }
                }

                try
                {
                    if (cmbAttAbsText.SelectedItem != null && cmbAttAbsText.SelectedItem is ChargeNumberAttribute)
                    {
                        worktime_.attributeId = ((ChargeNumberAttribute)cmbAttAbsText.SelectedItem).attributeId;
                    }
                    else
                    {
                        String message = "You must select an Attendance / Absence Type Test\n";
                        MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        isValid = false;
                    }
                }
                catch (Exception ex)
                {
                    String message = "Error occurred setting Attendance / Absence Type Test\n";
                    message += ex.Message;
                    MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    isValid = false;
                }

                try
                {
                    worktime_.timeStart = Common.SetDateTime(worktime_.Workday.workdayDate, maskedTxtTimeStart.Text.Trim());
                }
                catch (Exception ex)
                {
                    String message = "Error occurred setting TimeStart\n";
                    message += ex.Message;
                    MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    isValid = false;
                }

                try
                {
                    DateTime timeEnd = Common.SetDateTime(worktime_.Workday.workdayDate, maskedTxtTimeEnd.Text.Trim());
                    if (timeEnd > worktime_.timeStart)
                    {
                        TimeSpan timeDiff = timeEnd.Subtract(worktime_.timeStart);
                        worktime_.duration = timeDiff.Hours + (timeDiff.Minutes / 60.0);
                    }
                    else
                    {
                        String message = "End time must be greater than start time";
                        MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        isValid = false;
                    }
                }
                catch (Exception ex)
                {
                    String message = "Error occurred setting Duration\n";
                    message += ex.Message;
                    MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    isValid = false;
                }

                try
                {
                    worktime_.notes = txtNotes.Text.Trim();
                }
                catch (Exception ex)
                {
                    String message = "Error occurred setting Notes\n";
                    message += ex.Message;
                    MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    isValid = false;
                }

                try
                {
                    if (isValid)
                    {
                        if (chkFridayForward.Checked &&
                            worktime_.Workday.workdayDate.DayOfWeek == DayOfWeek.Friday &&
                            !chkFridayForwarded.Checked)
                        {
                            // Get a new (or existing) workday for next friday (e.g. add 7 days to the existing workday)
                            DateTime newDate = worktime_.Workday.workdayDate.AddDays(7.0);

                            var workday = (from w in workdayDb_.Workdays
                                           where w.employeeId == worktime_.Workday.employeeId &&
                                                 w.workdayDate.Year == newDate.Year &&
                                                 w.workdayDate.Month == newDate.Month &&
                                                 w.workdayDate.Day == newDate.Day &&
                                                 w.isDeleted == false
                                           select w).FirstOrDefault();
                            if (workday == null)
                            {
                                workday = new Workday();
                                workday.employeeId = worktime_.Workday.employeeId;
                                workday.workdayDate = newDate;
                                workday.isDeleted = false;
                                workdayDb_.Workdays.Add(workday);
                                workdayDb_.SaveChanges();
                            }

                            // okay now attach this worktime to this new workday
                            worktime_.workdayId = workday.workdayId;

                            // mark this as Friday Forwarded
                            worktime_.isFridayForwarded = true;

                            // okay, I think that is all I need to do...
                        }
                        else if (chkFridayForward.Checked)
                        {
                            String message = "Cannot forward this to next Friday because\n ";
                            if (!(worktime_.Workday.workdayDate.DayOfWeek == DayOfWeek.Friday))
                            {
                                message += "  - today is not friday\n";
                            }
                            if (chkFridayForwarded.Checked)
                            {
                                message += "  - this was already forwarded once before\n";
                            }
                            message += "Data NOT saved; please try again";
                            MessageBox.Show(message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                            chkFridayForward.Checked = false;
                            isValid = false;
                        }
                    }
                }
                catch (Exception ex)
                {
                    String message = "Error occurred forwarded time to next Friday\n";
                    message += ex.Message;
                    MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    isValid = false;
                }

                // uniqueness test
                try
                {
                    if (isValid)
                    {
                        DateTime timeEnd = worktime_.timeStart.AddHours(worktime_.duration);
                        var worktime = (from t in workdayDb_.Worktimes
                                        where t.workdayId == worktime_.workdayId &&
                                                ((t.timeStart == worktime_.timeStart) ||
                                                    (EntityFunctions.AddMinutes(t.timeStart, (int?)(t.duration * 60)) == timeEnd) ||
                                                    ((t.timeStart < worktime_.timeStart) &&
                                                        (
                                                            (
                                                                ((EntityFunctions.AddMinutes(t.timeStart, (int?)(t.duration * 60)) > worktime_.timeStart) &&
                                                                (EntityFunctions.AddMinutes(t.timeStart, (int?)(t.duration * 60)) < timeEnd))
                                                            ) ||
                                                            (EntityFunctions.AddMinutes(t.timeStart, (int?)(t.duration * 60)) > timeEnd)
                                                        )
                                                    ) ||
                                                    ((t.timeStart >= worktime_.timeStart) &&
                                                        (t.timeStart < timeEnd)
                                                    )
                                                ) &&
                                        t.isFridayForwarded == worktime_.isFridayForwarded && 
                                        t.worktimeId != worktime_.worktimeId &&
                                        t.isDeleted == false
                                        select t).FirstOrDefault();

                        if (worktime != null)
                        {
                            String message = "A record with this date already exists, please select another date.";
                            MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            isValid = false;
                        }
                    }
                }
                catch (Exception ex)
                {
                    String message = "Error occurred checking uniqueness\n";
                    message += ex.Message;
                    MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    isValid = false;
                }

                try
                {
                    if (isValid)
                    {
                        db_.SaveChanges();

                        // Refresh the database context
                        db_.Dispose();
                        workdayDb_.Dispose();
                        db_ = new RaytheonTimeDataEntities();
                        workdayDb_ = new RaytheonTimeDataEntities();

                        worktime_ = (from w in db_.Worktimes
                                     where w.worktimeId == worktime_.worktimeId
                                     select w).FirstOrDefault();

                        // update the parent's hours
                        var workHours = (from w in db_.Worktimes
                                         where w.workdayId == worktime_.workdayId &&
                                               w.isDeleted == false
                                         group w by new { w.workdayId } into grp
                                         select new { workHours = grp.Sum(w => w.duration) }).FirstOrDefault();
                        worktime_.Workday.hours = workHours.workHours;
                        db_.SaveChanges();

                        worktimeId_ = worktime_.worktimeId;
                        isNew_ = false;

                        db_.Dispose();
                        db_ = new RaytheonTimeDataEntities();

                        DataChangedCallback();
                        this.Close();
                    }
                }
                catch (Exception ex)
                {
                    String message = "Error occurred Saving\n";
                    message += ex.Message;
                    MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        #endregion
    }
}
